/**
 * Copyright 2008 Benjamin Possolo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.javenue.util.process;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Represents the persistent properties of an executing <tt>Process</tt>.
 * <p>
 * In order to guarantee that changes to a process state occur atomically and in a 
 * transactional fashion, a <tt>Process</tt> must wrap all modifications and reads
 * to its <tt>ProcessState</tt> object in a <tt>Transition</tt> or <tt>TransitionWithResult</tt>.
 * <p>
 * This class must be subclassed in order to add extra member variables 
 * that a <tt>Process</tt> is interested in persisting.
 * <p>
 * A <tt>ProcessStatePersister</tt> is responsible for actually persisting the
 * <tt>ProcessState</tt> objects to some sort of persistence store. The
 * <tt>ProcessStatePersister</tt> must be implemented by a user of this framework
 * and is most easily accomplished using some sort of O/R mapping framework
 * like Hibernate.
 * 
 * @see Process
 * @author Benjamin Possolo
 * <p>Created on May 10, 2008
 */
public abstract class ProcessState {
	
	//-----------------------------------------------------
	//Class variables
	//-----------------------------------------------------
	/**
	 * A logger that subclasses can also use.
	 */
	protected static final Logger log = LoggerFactory.getLogger(ProcessState.class);
	
	/**
	 * The default value for the <code>state</code> and <code>previousState</code> properties.
	 */
	public static final int UNINITIALIZED_STATE = -3000;
	
	//-----------------------------------------------------
	//Member variables
	//-----------------------------------------------------
	/**
	 * Represents a unique identifier.
	 */
	private Integer id = null;
	
	/**
	 * Represents the "current" state of the finite state machine.
	 */
	private int state = UNINITIALIZED_STATE;
	
	/**
	 * Represents the "previous" state of the finite state machine.
	 */
	private int previousState = UNINITIALIZED_STATE;

	//-----------------------------------------------------
	//Public methods
	//-----------------------------------------------------
	/**
	 * Generates a string that represents this object. The string contains the process
	 * state's ID value.
	 * 
	 * @return a string representation of this object
	 */
	@Override
	public String toString(){
		
		return "ProcessStateId:" + (id == null ? "null" : id.toString());
	}
	
	/**
	 * Gets the unique identifier.
	 * 
	 * @return the identifier
	 */
	public Integer getId(){
		return id;
	}
	
	/**
	 * Gets the previous state value. If the state has never been set via the setState(int)
	 * method, then this method returns the UNINITIALIZED_STATE value.
	 * 
	 * @return the previous state
	 */
	public int getPreviousState() {
		return previousState;
	}

	/**
	 * Gets the current state value. If the state has never been set via the setState(int)
	 * method, then this method returns the UNINITIALIZED_STATE value.
	 * 
	 * @return the current state
	 */
	public int getState() {
		return state;
	}
	
	//-----------------------------------------------------
	//Package protected methods
	//-----------------------------------------------------
	/**
	 * Sets the id.
	 * <p>
	 * This method will normally only be called by a <tt>TransitionManager</tt> through reflection.
	 * 
	 * @param id the unique id to set
	 */
	void setId(Integer id){
		this.id = id;
	}
	
	//-----------------------------------------------------
	//Protected methods
	//-----------------------------------------------------
	/**
	 * Sets the previous state. Normally this method will never need to be called since
	 * the <code>previousState</code> property is automatically updated whenever 
	 * the <code>setState(int newState)</code>
	 * method is called.
	 * 
	 * @param previousState the previous state value
	 */
	protected void setPreviousState(int previousState) {
		this.previousState = previousState;
	}
	
	/**
	 * Changes the state property. The current state value is automatically stored in the 
	 * <code>previousState</code> member variable.
	 * 
	 * @param newState the state to set
	 */
	protected void setState(int newState) {
		
		if( newState != state ){
			
			setPreviousState(state);
			state = newState;
			
			if( log.isDebugEnabled() && previousState != UNINITIALIZED_STATE ){
				String className = this.getClass().getName(); //get the runtime class name
				log.debug("[" + className + "] state change to [" + getStateAsString() + "]");
			}
		}
	}
	
	//-----------------------------------------------------
	//Abstract methods
	//-----------------------------------------------------
	
	/**
	 * Gets a string that describes the finite state machine's current state.
	 * 
	 * @return a string that describes the state
	 */
	public abstract String getStateAsString();
	
}
